/*******************************************************************************
 * Copyright (c) 2012-2016 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.plugin.svn.ide.commit;

import elemental.dom.Element;
import elemental.dom.Node;
import elemental.events.MouseEvent;
import elemental.html.TableCellElement;
import elemental.html.TableElement;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Singleton;

import org.eclipse.che.plugin.svn.ide.SubversionExtensionLocalizationConstants;
import org.eclipse.che.plugin.svn.ide.SubversionExtensionResources;
import org.eclipse.che.plugin.svn.shared.StatusItem;
import org.eclipse.che.ide.ui.Tooltip;
import org.eclipse.che.ide.ui.list.SimpleList;
import org.eclipse.che.ide.ui.menu.PositionController;
import org.eclipse.che.ide.ui.window.Window;
import org.eclipse.che.ide.util.dom.Elements;
import org.eclipse.che.ide.util.dom.MouseGestureListener;
import org.vectomatic.dom.svg.OMSVGSVGElement;

import javax.inject.Inject;
import java.util.List;

/**
 * Implementation of {@link CommitView}.
 *
 * @author Vladyslav Zhukovskyi
 */
@Singleton
public class CommitViewImpl extends Window implements CommitView {

    interface CommitViewImplUiBinder extends UiBinder<Widget, CommitViewImpl> {
    }

    private static CommitViewImplUiBinder uiBinder = GWT.create(CommitViewImplUiBinder.class);

    private ActionDelegate delegate;

    @UiField(provided = true)
    SubversionExtensionLocalizationConstants locale;

    @UiField(provided = true)
    SubversionExtensionResources resources;

    @UiField
    RadioButton commitAll;

    @UiField
    RadioButton commitSelection;

    @UiField
    CheckBox keepLocks;

    @UiField
    TextArea message;

    @UiField
    ScrollPanel changesWrapper;

    @UiField
    Label changedFilesCount;

    private SimpleList<StatusItem> changesList;

    private Button          btnCommit;
    private OMSVGSVGElement alertMarker;

    private static final String PLACEHOLDER = "placeholder";

    private static final String ADDED      = org.eclipse.che.ide.api.theme.Style.getVcsConsoleStagedFilesColor();
    private static final String CONFLICTED = org.eclipse.che.ide.api.theme.Style.getVcsConsoleErrorColor();
    private static final String DELETED    = org.eclipse.che.ide.api.theme.Style.getVcsConsoleErrorColor();
    private static final String MODIFIED   = org.eclipse.che.ide.api.theme.Style.getVcsConsoleModifiedFilesColor();
    private static final String REPLACED   = org.eclipse.che.ide.api.theme.Style.getVcsConsoleModifiedFilesColor();
    private static final String DEFAULT    = org.eclipse.che.ide.api.theme.Style.getMainFontColor();

    @Inject
    public CommitViewImpl(final SubversionExtensionLocalizationConstants constants,
                          final SubversionExtensionResources resources,
                          final Window.Resources windowResources,
                          org.eclipse.che.ide.Resources coreRes) {
        this.locale = constants;
        this.resources = resources;

        this.setTitle(locale.commitTitle());
        this.setWidget(uiBinder.createAndBindUi(this));

        Button btnCancel = createButton(locale.buttonCancel(), "svn-commit-cancel", new ClickHandler() {
            @Override
            public void onClick(final ClickEvent event) {
                delegate.onCancelClicked();
            }
        });

        btnCommit = createButton(locale.buttonCommit(), "svn-commit-commit", new ClickHandler() {
            @Override
            public void onClick(final ClickEvent event) {
                delegate.onCommitClicked();
            }
        });

        addButtonToFooter(btnCancel);
        addButtonToFooter(btnCommit);

        btnCommit.setEnabled(false);

        SimpleList.ListEventDelegate<StatusItem> listChangesDelegate = new SimpleList.ListEventDelegate<StatusItem>() {
            /** {@inheritDoc} */
            @Override
            public void onListItemClicked(Element listItemBase, StatusItem itemData) {
                //stub
            }

            /** {@inheritDoc} */
            @Override
            public void onListItemDoubleClicked(Element listItemBase, StatusItem itemData) {
                //stub
            }
        };

        TableElement changesElement = Elements.createTableElement();
        changesElement.setAttribute("style", "width: 100%; background: none;");
        changesElement.setCellSpacing("1px");
        changesList = SimpleList.create((SimpleList.View)changesElement, coreRes.defaultSimpleListCss(), new ChangesListRenderer(),
                                        listChangesDelegate);

        changesWrapper.add(changesList);

        message.getElement().setAttribute(PLACEHOLDER, locale.commitPlaceholder());

        alertMarker = resources.alert().getSvg();
        alertMarker.getStyle().setWidth(22, Style.Unit.PX);
        alertMarker.getStyle().setHeight(22, Style.Unit.PX);
        alertMarker.getStyle().setMarginTop(5, Style.Unit.PX);
        getFooter().getElement().appendChild(alertMarker.getElement());

        Tooltip.create((elemental.dom.Element)alertMarker.getElement(),
                       PositionController.VerticalAlign.TOP,
                       PositionController.HorizontalAlign.MIDDLE,
                       locale.commitMessageEmpty());

        alertMarker.getStyle().setVisibility(Style.Visibility.VISIBLE);
    }

    /** {@inheritDoc} */
    @Override
    public void onClose() {
        hide();
    }

    /** {@inheritDoc} */
    @Override
    public void setDelegate(final ActionDelegate delegate) {
        this.delegate = delegate;
    }

    /** {@inheritDoc} */
    @Override
    public void onShow() {
        commitAll.setValue(true);
        commitSelection.setValue(false);
        show();
    }

    /** {@inheritDoc} */
    @Override
    public String getMessage() {
        return this.message.getText();
    }

    /** {@inheritDoc} */
    @Override
    public boolean isKeepLocksStateSelected() {
        return keepLocks.getValue();
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCommitAllSelected() {
        return commitAll.getValue();
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCommitSelectionSelected() {
        return commitSelection.getValue();
    }

    /** {@inheritDoc} */
    @Override
    public void setChangesList(List<StatusItem> changes) {
        changesList.render(changes);
        changedFilesCount.setText(String.valueOf(changes.size()));
    }

    @UiHandler("message")
    @SuppressWarnings("unused")
    public void onMessageChanged(KeyUpEvent event) {
        btnCommit.setEnabled(!message.getText().isEmpty());

        alertMarker.getStyle().setVisibility(!message.getText().isEmpty() ? Style.Visibility.HIDDEN : Style.Visibility.VISIBLE);
    }

    @UiHandler({"commitAll", "commitSelection"})
    @SuppressWarnings("unused")
    public void onCommitModeChanged(ClickEvent event) {
        delegate.onCommitModeChanged();
    }

    private class ChangesListRenderer extends SimpleList.ListItemRenderer<StatusItem> {

        /** {@inheritDoc} */
        @Override
        public void render(Element listItemBase, StatusItem itemData) {
            Node changedItemStatusNote = getChangedItemStatus(itemData);
            Node changedItemPath = getChangedItemPath(itemData);
            Node changedItemDiffLink = getChangedItemDiffLink(itemData);

            listItemBase.appendChild(changedItemStatusNote);
            listItemBase.appendChild(changedItemPath);
            listItemBase.appendChild(changedItemDiffLink);
        }

        /** {@inheritDoc} */
        @Override
        public Element createElement() {
            return Elements.createTRElement();
        }

        private Node getChangedItemStatus(StatusItem item) {
            TableCellElement htmlNode = Elements.createTDElement();
            htmlNode.setInnerText(item.getFileState().getValue());
            htmlNode.setWidth("16px");
            htmlNode.setAlign("center");
            htmlNode.getStyle().setPaddingTop("3px");
            htmlNode.getStyle().setFontSize("11px");
            return htmlNode;
        }

        private Node getChangedItemPath(StatusItem item) {
            final String[] pathElements = item.getPath().split("/");

            final StringBuilder sb = new StringBuilder();
            //append file name
            sb.append("<span style=\"color:")
              .append(getChangedItemColor(item))
              .append("\">")
              .append(new SafeHtmlBuilder().appendEscaped(pathElements[pathElements.length - 1]).toSafeHtml().asString())
              .append("</span>");

            //append path
            if (pathElements.length > 1) {
                String rawPath = item.getPath().substring(0, item.getPath().length() - pathElements[pathElements.length - 1].length() - 1);
                String escapedPath = new SafeHtmlBuilder().appendEscaped(rawPath).toSafeHtml().asString();

                sb.append(" <span>(");
                sb.append(escapedPath);
                sb.append(")</span>");
            }

            final String html = sb.toString();
            TableCellElement htmlNode = Elements.createTDElement();
            htmlNode.setInnerHTML(html);
            htmlNode.getStyle().setProperty("max-width", "200px");
            htmlNode.getStyle().setProperty("text-overflow", "ellipsis");

            return htmlNode;
        }

        private Node getChangedItemDiffLink(final StatusItem item) {

            Button showDiff = new Button();
            showDiff.setText("Diff");

            TableCellElement htmlNode = Elements.createTDElement();
            htmlNode.appendChild((Element)showDiff.getElement());
            htmlNode.setWidth("1px");

            if (!(item.getFileState() == StatusItem.FileState.MODIFIED
                  || item.getFileState() == StatusItem.FileState.DELETED
                  || item.getFileState() == StatusItem.FileState.CONFLICTED)) {
                showDiff.setEnabled(false);

                Tooltip.create((Element)showDiff.getElement(),
                               PositionController.VerticalAlign.MIDDLE,
                               PositionController.HorizontalAlign.LEFT,
                               locale.commitDiffUnavailable());
            }

            MouseGestureListener.createAndAttach((Element)showDiff.getElement(), new MouseGestureListener.Callback() {
                /** {@inheritDoc} */
                @Override
                public boolean onClick(int clickCount, MouseEvent event) {
                    delegate.showDiff(item.getPath());
                    return false;
                }

                /** {@inheritDoc} */
                @Override
                public void onDrag(MouseEvent event) {
                    //stub
                }

                /** {@inheritDoc} */
                @Override
                public void onDragRelease(MouseEvent event) {
                    //stub
                }
            });

            return htmlNode;
        }

        private String getChangedItemColor(StatusItem item) {
            switch (item.getFileState()) {
                case ADDED:
                    return ADDED;
                case CONFLICTED:
                    return CONFLICTED;
                case DELETED:
                    return DELETED;
                case MODIFIED:
                    return MODIFIED;
                case REPLACED:
                    return REPLACED;
                default:
                    return DEFAULT;
            }
        }
    }
}
